package common

import (
	"encoding/json"
	"power/core/localwrap"

	"github.com/RocksonZeta/wrap/errs"
	"github.com/RocksonZeta/wrap/utils/nutil"
)

var glog = localwrap.GetLogger().WithPkg("core/service/common")

func Unmarshal(str string, obj interface{}) {
	err := json.Unmarshal([]byte(str), obj)
	if err != nil {
		panic(errs.Err{Err: err, Message: err.Error(), State: 1, Pkg: "power/core/service/common"})
	}
}
func Marshal(obj interface{}) string {
	bs, err := json.Marshal(obj)
	if err != nil {
		panic(errs.Err{Err: err, Message: err.Error(), State: 1, Pkg: "power/core/service/common"})
	}
	return string(bs)
}

type Graph struct {
	Version  string               `json:"version"`
	Refs     map[string]ClassPath `json:"refs,omitempty"`
	Datas    []GraphElement       `json:"datas,omitempty"`
	Scale    float64              `json:"scale,omitempty"`
	TX       float64              `json:"tx,omitempty"`
	TY       float64              `json:"ty,omitempty"`
	Editable bool                 `json:"editable,omitempty"`
}

func (g Graph) Json() string {
	bs, err := json.Marshal(g)
	if err != nil {
		glog.Error().Err(err).Msg(err.Error())
	}
	return string(bs)
}

type GraphProps struct {
	Version  string               `json:"version"`
	Refs     map[string]ClassPath `json:"refs,omitempty"`
	Scale    float64              `json:"scale"`
	TX       float64              `json:"tx"`
	TY       float64              `json:"ty"`
	Editable bool                 `json:"editable"`
}

type GraphElement struct {
	ClassName string           `json:"_className"`
	RefId     int              `json:"_refId"`
	JSON      GraphElementJSON `json:"json"`
}

func (e GraphElement) SetId(id int) {
	if nil == e.JSON.Properties {
		e.JSON.Properties = make(map[string]interface{})
	}
	e.JSON.Properties["ID"] = id
}
func (e GraphElement) GetId() int {
	if nil == e.JSON.Properties {
		return 0
	}
	return e.JSON.Properties["ID"].(int)
}

type GraphElementJSON struct {
	Name           string           `json:"name"`
	Location       Props            `json:"location,omitempty"`
	Size           Props            `json:"size,omitempty"` //GraphElementSize {width:1,height:1}
	ZIndex         int              `json:"zIndex,omitempty"`
	Rotate         float64          `json:"rotate,omitempty"`
	TooltipType    string           `json:"tooltipType,omitempty"`
	AnchorPosition Props            `json:"anchorPosition,omitempty"` //Position  ClassPath {"_classPath": "Q.Position.LEFT_TOP"}
	Parent         Props            `json:"parent,omitempty"`         //NodeRef {_ref:1}
	Host           Props            `json:"host,omitempty"`           //NodeRef {_ref:1}
	Image          string           `json:"image,omitempty"`          //can be url or Q.Path GraphElement
	Styles         Props            `json:"styles,omitempty"`         //map
	Properties     Props            `json:"properties,omitempty"`     //map
	BindingUIs     []GraphElementUI `json:"bindingUIs,omitempty"`
	From           Props            `json:"from,omitempty"` //NodeRef {_ref:1}
	To             Props            `json:"to,omitempty"`   //NodeRef {_ref:1}
	EdgeType       string           `json:"edgeType,omitempty"`
	Padding        int              `json:"padding,omitempty"`
	GroupImage     Props            `json:"groupImage,omitempty"` // ClassPath {"_classPath": "graphs.group_cloud"}
	GroupType      string           `json:"groupType,omitempty"`  // ELLIPSE
	// Movable        bool             `json:"movable,omitempty"`
}

func ParseGraphProps(def string) (GraphProps, error) {
	var r GraphProps
	err := json.Unmarshal([]byte(def), &r)
	return r, err
}
func ParseGraphElement(def string) (GraphElement, error) {
	var r GraphElement
	err := json.Unmarshal([]byte(def), &r)
	return r, err
}

type GraphElementLocation struct {
	X              float64 `json:"x"`
	Y              float64 `json:"y"`
	InvalidateFlag bool    `json:"invalidateFlag,omitempty"`
}

func (e GraphElementLocation) Map() Props {
	return map[string]interface{}{
		"x":              e.X,
		"y":              e.Y,
		"invalidateFlag": e.InvalidateFlag,
	}
}

type GraphElementUI struct {
	UI                GraphElementUIJSON `json:"ui"`
	BindingProperties Props              `json:"bindingProperties"`
}

type GraphElementUIJSON struct {
	ClassName string                 `json:"_className"`
	JSON      map[string]interface{} `json:"json"`
}
type GraphElementSize struct {
	Width  int `json:"width,omitempty"`
	Height int `json:"height,omitempty"`
}

func (e GraphElementSize) Map() Props {
	return map[string]interface{}{
		"width":  e.Width,
		"height": e.Height,
	}
}

type Props map[string]interface{}

func (p Props) NodeRef() NodeRef {
	return NodeRef{Ref: nutil.ValueOf(p["_ref"]).AsInt()}
}
func (p Props) ClassPath() ClassPath {
	return ClassPath{ClassPath: nutil.ValueOf(p["_classPath"]).String()}
}
func (p Props) GraphElementSize() GraphElementSize {
	return GraphElementSize{Width: nutil.ValueOf(p["width"]).AsInt(), Height: nutil.ValueOf(p["height"]).AsInt()}
}
func (p Props) PathElement() PathElement {
	return PathElement{Type: nutil.ValueOf(p["type"]).String(), Points: p["points"].([]float64)}
}
func (p Props) GraphElementLocation() GraphElementLocation {
	return GraphElementLocation{X: nutil.ValueOf(p["x"]).AsFloat64(), Y: nutil.ValueOf(p["y"]).AsFloat64(), InvalidateFlag: nutil.ValueOf(p["invalidateFlag"]).Bool()}
}

type NodeRef struct {
	Ref int `json:"_ref"`
}

func (n NodeRef) Props() Props {
	return map[string]interface{}{
		"_ref": n.Ref,
	}
}

type PathElement struct {
	Type   string    `json:"type"`
	Points []float64 `json:"points"`
}

func (e PathElement) Props() Props {
	return map[string]interface{}{
		"type":   e.Type,
		"points": e.Points,
	}
}

// {
// 	"_classPath": "Q.Position.LEFT_TOP"
// }
type ClassPath struct {
	ClassPath string `json:"_classPath"`
}

func (e ClassPath) Props() Props {
	return map[string]interface{}{
		"_ref": e.ClassPath,
	}
}

const (
	ElementTypeNode = 1 + iota
	ElementTypeEdge
	ElementTypeGroup
	ElementTypeText
	ElementTypeLabel

	ElementTypeNodeUI = 1 + iota
	ElementTypeEdgeUI
	ElementTypeGroupUI
	ElementTypeLabelUI
	ElementTypeImageUI

	ElementEdgeTypeDefault    = ""
	ElementEdgeTypeOrthogonal = "orthogonal"
	ElementEdgeTypeElbow      = "elbow"
	ElementEdgeTypeZigzag     = "zigzag"

	ElementGroupTypeCircle  = "circle"
	ElementGroupTypeELLIPSE = "ELLIPSE"
	ElementGroupTypeRect    = "rect"
)

const (
	QNode  = "Q.Node"
	QEdge  = "Q.Edge"
	QGroup = "Q.Group"
	QText  = "Q.Text"
	QLabel = "Q.Label"
)

var ElementTypeQuneeMap = map[string]int{
	QNode:  ElementTypeNode,
	QEdge:  ElementTypeEdge,
	QGroup: ElementTypeGroup,
	QText:  ElementTypeText,
	QLabel: ElementTypeLabel,
}
var ElementUITypeQuneeMap = map[string]int{
	"Q.NodeUI":  ElementTypeNodeUI,
	"Q.EdgeUI":  ElementTypeEdgeUI,
	"Q.GroupUI": ElementTypeGroupUI,
	"Q.LabelUI": ElementTypeLabelUI,
	"Q.ImageUI": ElementTypeImageUI,
}
